package com.qfedu.edu.filter;

import com.alibaba.fastjson2.JSON;
import com.qfedu.edu.bo.Account;
import com.qfedu.edu.bo.SecurityUser;
import com.qfedu.edu.context.Context;
import com.qfedu.edu.manager.RedisManager;
import com.qfedu.edu.result.R;
import com.qfedu.edu.result.ResponseCode;
import com.qfedu.edu.service.ITokenService;
import com.qfedu.edu.utils.HttpResponseUtils;
import com.qfedu.edu.vo.LoginVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author xiaobobo
 * @title: loginFilter
 * @projectName cd-fy-2401-third-project-parent
 * @description: 重写的是这个登陆的过滤器
 * @date 2024/9/13  10:32
 */
@Slf4j
public class LoginFilter extends UsernamePasswordAuthenticationFilter {

    private AuthenticationManager authenticationManager;

    private ITokenService tokenService;

    private RedisManager redisManager;

    private Integer expire;


    /**
     * 这里通过构造器进行注入
     * @param authenticationManager
     */
    public LoginFilter(AuthenticationManager authenticationManager,ITokenService tokenService,RedisManager redisManager,Integer expire){
        this.authenticationManager=authenticationManager;
        this.tokenService=tokenService;
        this.redisManager=redisManager;
        this.expire=expire;
        //这里还需要设置这个登陆的地址是啥
        this.setRequiresAuthenticationRequestMatcher(
                new AntPathRequestMatcher("/iotserver/user/login", HttpMethod.POST.name()));
    }

    /**
     * 登陆的方法
     * @param request
     * @param response
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        //到这里首先要封装请求参数回来
        LoginVo loginVo=getAccountInfo(request);
        //接下来：是不是要将这个用户信息和密码信息以及type封装成一个对象 UsernameAndPasswordToken对象
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(JSON.toJSONString(loginVo), loginVo.getPassword());
        //接下来就可以调用AuthenticationManager来完成登陆了
        return authenticationManager.authenticate(usernamePasswordAuthenticationToken);
    }

    /**
     * 从请求中获取账户信息
     * @param request
     * @return
     */
    private LoginVo getAccountInfo(HttpServletRequest request) {
        try {
            request.setCharacterEncoding("UTF-8");
            ServletInputStream inputStream = request.getInputStream();
            byte[] buf=new byte[8192];
            int read = inputStream.read(buf);
            String val = new String(buf, 0, read, "UTf-8");
            //将这个值 转换成对象
            LoginVo loginVo = JSON.parseObject(val, LoginVo.class);
            return loginVo;
        } catch (Exception e) {
            throw new RuntimeException("获取前端传输参数有错");
        }
    }


    /**
     * 登陆成功的回调
     * @param request
     * @param response
     * @param chain
     * @param authResult
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        //登陆成功那么就会执行到这里来
        //这里生成token 生成token之前首先拿到这个id和名字
        SecurityUser securityUser = (SecurityUser) authResult.getPrincipal();
        //这个就是咋们的权限和角色的集合
        List<String> roleAndPerms = securityUser.getRoleAndPerms();
        //取出id 用户名
        Account account = securityUser.getAccount();
        String id = account.getId();
        String username = account.getUsername();
        Integer type = account.getType();
        //接下来就可以生成token了
        String token = tokenService.createToken(id, username);
        log.info("生成的token信息是:"+token);
        //我们就要将这个权限信息和角色信息存储到Redis中 所以我们改个地方 内存中
//        Context.getRoleAndPermMap().put(token,roleAndPerms);
        //今天将权限信息和角色信息放到Redis中去(登陆修改的第一个地方)
        redisManager.addString(token,JSON.toJSONString(roleAndPerms),this.expire, TimeUnit.DAYS);
        //最后要将这个token信息还有id还有username的信息返回给客户端
        R r = R.ok().data("token", token).data("userId", id).data("username", username);
        //然后调用响应的方法去返回给客户端
        HttpResponseUtils.sendResponse(response, r);
    }


    /**
     * 登陆失败的回调函数
     * @param request
     * @param response
     * @param failed
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        //登陆失败就会执行到这里来
        R error = R.error(ResponseCode.LOGIN_EXCEPTION);
        HttpResponseUtils.sendResponse(response,error);
    }
}
